package com.dragon.blelibrary.scandevise;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

import androidx.annotation.RequiresApi;

import com.dragon.blelibrary.bleutils.BleLOG;
import com.dragon.blelibrary.bleutils.ParserUtils;
import com.dragon.blelibrary.bleutils.ScheduledExecutorUtil;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;

/**
 * BLE蓝牙搜索类，包含判断蓝牙是否有效，包含启动搜索蓝牙，包含停止搜索蓝牙等操作 com.lenovo.vb10.scan.BleDeviceScan
 * 
 * @author allow.xuweibin <br/>
 *         create in 2014年12月5日 下午10:02:26
 */
public class BleDeviceScan extends DeviceScan implements Handler.Callback {
	
    private static final String TAG = BleDeviceScan.class.getSimpleName();
    
    private static boolean NEED_FILTER = true;// 是否需要过滤
    private static final String FENDA_MAC = "F0:13:C3";
    private final MyScanCallback scanCallback;
    private int RSSI = -1000;

    private List<IScanListener> scanListeners;
    private Set<String> mDeviceAddress = new HashSet<String>();
    private boolean isScanning = false;
    private Handler mHandler;
    
    private static final String EXTRA_DEVICE = "device";
    private static final String EXTRA_NAME   = "name";
    private static final String EXTRA_RSSI   = "rssi";
    private static final String EXTRA_RECORD = "scan_record";
    
    private static final int MSG_SCAN_TIMEOUT    = 0;
    private static final int MSG_SCAN_NEW_DEVICE = 1;
    
    private static BleDeviceScan instance;
    
    public static BleDeviceScan getInstance(Context context) {
    	if(null == instance) {
    		synchronized (BleDeviceScan.class) {
    			if(null == instance) {
    				instance = new BleDeviceScan(context.getApplicationContext());
    			}
			}
    	}
    	return instance;
    }

    private BleDeviceScan(Context context) {
        super(context);
        this.mHandler = new Handler(Looper.getMainLooper(), this);
        scanListeners = new ArrayList<IScanListener>();
        scanCallback= new MyScanCallback();
    }

	@Override
	public boolean handleMessage(Message msg) {
		switch (msg.what) {
		case MSG_SCAN_TIMEOUT:
            if (!isScanning) {
                return true;
            }
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
                if(mAdapter != null && mAdapter.getBluetoothLeScanner() != null)
                mAdapter.getBluetoothLeScanner().stopScan(scanCallback);
            else
                mAdapter.stopLeScan(mLeScanCallback);
            isScanning = false;
            if (mDeviceAddress.size() == 0) {
            	notifyScanResult(IScanListener.RESULT_SCAN_NO_DEVICE);
            } else {
            	notifyScanResult(IScanListener.RESULT_SCAN_TIME_OUT);
            }
            mDeviceAddress.clear();
            scanListeners.clear();
			break;
		case MSG_SCAN_NEW_DEVICE:
			Bundle bundle = msg.getData();
			BluetoothDevice device = bundle.getParcelable(EXTRA_DEVICE);
			String deviceName = bundle.getString(EXTRA_NAME);
			int rssi = bundle.getInt(EXTRA_RSSI);
			byte[] scanRecord = bundle.getByteArray(EXTRA_RECORD);
            for(int i = 0; i < scanListeners.size(); i++) {
                scanListeners.get(i).OnScanNewDevice(device, deviceName, rssi, scanRecord);
            }
			break;

		default:
			break;
		}
		return false;
	}
	
	private void notifyScanResult(int result) {
        for(int i = 0; i < scanListeners.size(); i++) {
            scanListeners.get(i).OnScanEnd(result);
        }
	}
    /**
     * @param scanTime
     *            搜索超时时间
     * @param listener
     *            搜索监听回调
     */
    public boolean scanDevice(long scanTime, IScanListener listener) {
    	return scanDevice(scanTime, listener, true);
    }

    /**
     * @param scanTime
     *            搜索超时时间
     * @param listener
     *            搜索监听回调
     * @param needFilter 是否过滤蓝牙名称
     * @param uuidArr 是否过滤uuid
     */
    public boolean scanDevice(long scanTime, IScanListener listener, boolean needFilter, UUID... uuidArr) {
    	NEED_FILTER = needFilter;
        scanListeners.add(listener);
        RSSI = -1000;
        mDeviceAddress.clear();
        
        if (listener == null) {
            return false;
        }
        if (isScanning) {
        	for(int i = 0; i < scanListeners.size(); i++) {
        		scanListeners.get(i).OnScanning();
        	}
            BleLOG.w(TAG, "scanDevice(,), isScanning...");
            return false;
        }
        mHandler.removeMessages(MSG_SCAN_TIMEOUT);
        mHandler.sendEmptyMessageDelayed(MSG_SCAN_TIMEOUT, scanTime);
        
//        mAdapter.startLeScan(mLeScanCallback);
        isScanning = true;
        //放在子线程中搜索
        ScheduledExecutorUtil.getService().execute(new Runnable() {
            @Override
            public void run() {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
                    if(mAdapter != null && mAdapter.getBluetoothLeScanner() != null){
                        ScanSettings settings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
                        mAdapter.getBluetoothLeScanner().startScan(null, settings, scanCallback);//.startLeScan(mLeScanCallback);
                    }
                else
                    mAdapter.startLeScan(mLeScanCallback);
                BluetoothManager bManager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
                int[] states = new int[]{
                        BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_CONNECTING };
                List<BluetoothDevice> devices = bManager.getDevicesMatchingConnectionStates(BluetoothProfile.GATT, states);
                if(null != devices) {
                    BleLOG.i(TAG, "Connected device count:" + devices.size());
                } else {
                    BleLOG.i(TAG, "Connected device count:0");
                }
                if(null != devices && devices.size() > 0) {
                    for(int i = 0; i < devices.size(); i++) {
                        String name = devices.get(i).getName();
                        BleLOG.i(TAG, "Connected device[" + i + "] - name:" + devices.get(i).getName() + ", address:" + devices.get(i).getAddress());
                        final BluetoothDevice connectedDevice = devices.get(i);
                        mHandler.post(new Runnable() {
                            @RequiresApi(api = Build.VERSION_CODES.O)
                            @Override
                            public void run() {
                                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
                                    scanCallback.onLeScan(connectedDevice, - 1, null);
                                }else{
                                    mLeScanCallback.onLeScan(connectedDevice, - 1, null);
                                }
                            }
                        });
                    }
                }
            }
        });



        return true;
    }

    @Override
    public void stopScanDevice() {
        // TODO Auto-generated method stub
    	if (isScanning) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
                if(mAdapter != null && mAdapter.getBluetoothLeScanner() != null)
                mAdapter.getBluetoothLeScanner().stopScan(scanCallback);
            else
    		    mAdapter.stopLeScan(mLeScanCallback);
    	}
        mDeviceAddress.clear();
        mHandler.removeMessages(MSG_SCAN_TIMEOUT);
        isScanning = false;
    	notifyScanResult(IScanListener.RESULT_STOP_BY_HUMAN);
        scanListeners.clear();
    }

    private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
        @Override
        public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
            addDevice(device,rssi,scanRecord);
        }
    };
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private class MyScanCallback extends ScanCallback {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            super.onScanResult(callbackType, result);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                BluetoothDevice device = result.getDevice();
                int rssi = result.getRssi();
                byte[] scanRecord = null;
                if(result.getScanRecord() != null){
                    scanRecord = result.getScanRecord().getBytes();
                }
                addDevice(device,rssi,scanRecord);
            }

        }
        public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord){
            addDevice(device,rssi,scanRecord);
        }
    }


    /**
     * 添加搜索到的设备
     * @param device BluetoothDevice
     * @param rssi int
     * @param scanRecord byte[]
     */
    private void addDevice(BluetoothDevice device, int rssi, byte[] scanRecord){
        if (mDeviceAddress.contains(device.getAddress())) {
            return;
        }
        String deviceName = device.getName();
        if(null != scanRecord && deviceName == null) {
            deviceName = ScannerServiceParser.decodeDeviceName(scanRecord);
            String scanData = ParserUtils.parse(scanRecord);
            if(deviceName == null)
                Log.i(TAG,"name = " + deviceName + "--- mac =" + device.getAddress() + "||"+scanData);
        }

        BleLOG.w(TAG, "name: " + deviceName + ", mac: " + device.getAddress() + ", rssi: " + rssi);
        mDeviceAddress.add(device.getAddress());

        Bundle bundle = new Bundle();
        bundle.putParcelable(EXTRA_DEVICE, device);
        bundle.putString(EXTRA_NAME, deviceName);
        bundle.putInt(EXTRA_RSSI, rssi);
        bundle.putByteArray(EXTRA_RECORD, scanRecord);

        Message msg = Message.obtain();
        msg.what = MSG_SCAN_NEW_DEVICE;
        msg.setData(bundle);
        mHandler.sendMessage(msg);
    }
    /**
     * In fact, it calls the method {@linkplain BluetoothAdapter#getRemoteDevice(String) getRemoteDevice(String)}
     */
    @Override
    public BluetoothDevice getDevice(String mac) {
        final BluetoothDevice device = mAdapter.getRemoteDevice(mac);
        return device;
    }

	@Override
	public boolean isScanning() {
		return isScanning;
	}
}
